package net.huashitong.Indicators.utils;

import com.sirdc.modules.utils.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

public class WorkbookUtil {
    public static final BigDecimal BIGDECIMAL_MAXVALUE = BigDecimal.valueOf(99999999999.9999);
    public static final BigDecimal BIGDECIMAL_MINVALUE = BigDecimal.valueOf(-99999999999.9999);

    public static final BigDecimal BIGDECIMAL_MAXVALUE_S2 = BigDecimal.valueOf(99999999999.99);
    public static final BigDecimal BIGDECIMAL_MINVALUE_S2 = BigDecimal.valueOf(-99999999999.99);

    public static final BigDecimal BIGDECIMAL_MAXVALUE_S3 = BigDecimal.valueOf(99999999999.999);
    public static final BigDecimal BIGDECIMAL_MINVALUE_S3 = BigDecimal.valueOf(-99999999999.999);

    /**
     * 以字符串格式获取单元格内容
     *
     * @param sheet
     *            工作表
     * @return 单元格内容
     * @throws Exception
     */
    public static List<String> getCaptions(Sheet sheet) throws Exception {
        List<String> captions = new ArrayList<String>();
        Row captionRow = sheet.getRow(0);
        for (int col = captionRow.getFirstCellNum(); col < captionRow.getLastCellNum(); col++) {
            captions.add(captionRow.getCell(col).getStringCellValue());
        }
        return captions;
    }


    /**
     * 根据导入文件参数获取一个Workbook对象
     * @param file
     * @return
     * @throws Exception
     */
    public static Workbook getWorkbook(String file) throws Exception {
        Workbook book = null;
        FileInputStream fs = null;
        try {
            fs = new FileInputStream(file);
            if (file.endsWith(".xlsx")) {// Excel 2007
                book = new XSSFWorkbook(fs);
            } else {// Excel 2003
                book = new HSSFWorkbook(fs);
            }
        } catch (IOException e) {
        } finally {
            fs.close();
        }
        return book;
    }


    /**
     *
     * 根据导入文件参数获取一个Workbook对象
     * 导入文件参数
     * @param book
     * @param filename
     * @throws Exception
     */
    public static void writeWorkbook(Workbook book, String filename) throws Exception {
        FileOutputStream fos = null;
        try {
            File file = new File(filename);
            if (file.exists() == false) {
                file.createNewFile();
            }
            fos = new FileOutputStream(filename);
            book.write(fos);// 创建结果文件
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fos.close();
        }
    }

    public static void writeCell(Cell cell, Object value) {
        if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Integer) {
            cell.setCellValue((Integer) value);
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof Date) {
            cell.setCellValue((Date) value);
        } else if (value instanceof BigDecimal) {
            cell.setCellValue(((BigDecimal) value).doubleValue());
        }
    }

    /**
     * 获取指定标题的列号
     *
     * @param sheet
     *            工作表
     * @param field
     *            列标题
     * @return 列号
     * @throws Exception
     */
    public static int getFieldCol(Sheet sheet, String field) throws Exception {
        if (StringUtils.isBlank(field)) {
            throw new Exception("列标题不能为空。");
        }

        int colIdx = 0;
        Row captionRow = sheet.getRow(0);
        while (colIdx < captionRow.getLastCellNum()) {
            Cell cell = captionRow.getCell(colIdx);

            if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC && field.equals(cell.getNumericCellValue())) {// Excel中纯数值的单元格类型不再是String
                return colIdx;
            } else if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING
                    || cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {// XSSFCell与HSSFCell值相同
                if (StringUtils.isBlank(cell.getStringCellValue()) == false
                        && field.equals(cell.getStringCellValue())) {
                    return colIdx;
                }
            } else if (cell.getCellType() == HSSFCell.CELL_TYPE_BOOLEAN) {
                if (field.equals(cell.getBooleanCellValue())) {
                    return colIdx;
                }
            }

            colIdx++;
        }
        throw new Exception("无法识别的字段(" + field + ")。");
    }

    /**
     * 判断是否空记录。
     *
     * @param row
     * @return
     */
    public static boolean isRowEmpty(Row row) {
        if (row == null) {
            return true;
        }
        Iterator it = row.cellIterator();
        while (it.hasNext()) {
            Cell cell = (Cell) it.next();

            if (cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
                continue;
            } else if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING
                    || cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
                if (StringUtils.isBlank(cell.getStringCellValue())) {
                    continue;
                }
            }
            return false;
        }
        return true;
    }

    /**
     * 以字符串格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @return 单元格内容
     * @throws Exception
     */
    public static String getStringCellValue(Row row, String field) throws Exception {
        return getStringCellValue(row, field, true, Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    /**
     * 以字符串格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空
     * @return 单元格内容
     * @throws Exception
     */
    public static String getStringCellValue(Row row, String field, boolean nullable) throws Exception {
        return getStringCellValue(row, field, nullable, Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    /**
     * 以字符串格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空
     * @param pattern
     *            模式，用正则表达式表示
     * @param patternPrompt
     *            模式不匹配时的提示信息
     * @return 单元格内容
     * @throws Exception
     */
    public static String getStringCellValue(Row row, String field, boolean nullable, String pattern,
                                            String patternPrompt) throws Exception {
        return getStringCellValue(row, field, nullable, Integer.MIN_VALUE, Integer.MAX_VALUE, pattern, patternPrompt);
    }

    private static Cell getCell(Row row, String field) throws Exception {
        int colIndex = getFieldCol(row.getSheet(), field);

        if (row.getSheet().getNumMergedRegions() > 0) {
            Cell cell = getMergedRegionCell(row.getSheet(), row.getRowNum(), colIndex);
            if (cell != null) {
                return cell;
            }
        }

        return row.getCell(colIndex);
    }

    /**
     * 获取合并单元格
     *
     * @param sheet
     * @param row
     * @param column
     * @return
     */
    private static Cell getMergedRegionCell(Sheet sheet, int row, int column) {
        int sheetMergeCount = sheet.getNumMergedRegions();

        for (int i = 0; i < sheetMergeCount; i++) {
            CellRangeAddress ca = sheet.getMergedRegion(i);
            int firstColumn = ca.getFirstColumn();
            int lastColumn = ca.getLastColumn();
            int firstRow = ca.getFirstRow();
            int lastRow = ca.getLastRow();

            if (row >= firstRow && row <= lastRow) {

                if (column >= firstColumn && column <= lastColumn) {
                    Row fRow = sheet.getRow(firstRow);
                    return fRow.getCell(firstColumn);
                }
            }
        }

        return null;
    }

    /**
     * 以字符串格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空
     * @param min
     *            最小长度，允许等于
     * @param max
     *            最大长度，允许等于
     * @param pattern
     *            模式，用正则表达式表示
     * @param patternPrompt
     *            模式不匹配时的提示信息
     * @return 单元格内容
     * @throws Exception
     */
    public static String getStringCellValue(Row row, String field, boolean nullable, int min, int max, String pattern,
                                            String patternPrompt) throws Exception {
        Cell cell = null;
        try {
            cell = getCell(row, field);
        } catch (Exception e) {
        }
        String value = "";
        if (cell == null) {// 单元格内容为空时，cell=null
            if (nullable == false) {
                throw new Exception(field + "不能为空");
            }
            return null;
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
            if (nullable == false)
                throw new Exception(field + "不能为空");
            return null;
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {// Excel中纯数值的单元格类型不再是String
            NumberFormat format = NumberFormat.getInstance();
            format.setMinimumFractionDigits(0);// 设置数值的小数部分允许的最小位数。
            format.setMaximumFractionDigits(9);// 设置数值的小数部分允许的最大位数。
            value = format.format(cell.getNumericCellValue());// 将末尾的0去掉。使单元格内容“1”，读取到的“1.00”变成“1”
            value = value.replace(",", "");
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING
                || cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {// XSSFCell与HSSFCell值相同
            if (nullable == false && StringUtils.isBlank(cell.getStringCellValue())) {
                throw new Exception(field + "不能为空");
            }
            value = cell.getStringCellValue();
        }

        if (value.length() < min) {
            throw new IllegalArgumentException(field + "长度必须大于等于\"" + min + "\"");
        }
        if (value.length() > max) {
            throw new IllegalArgumentException(field + "长度必须小于等于\"" + max + "\"");
        }

        if (StringUtils.isBlank(pattern) == false) {// 正则表达式验证
            if (Pattern.matches(pattern, value) == false) {
                throw new IllegalArgumentException(field + "格式不合法，" + patternPrompt);
            }
        }
        return value.trim();
    }

    /**
     * 以字符串格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空
     * @param min
     *            最小长度，允许等于
     * @param max
     *            最大长度，允许等于
     * @return 单元格内容
     * @throws Exception
     */
    public static String getStringCellValue(Row row, String field, boolean nullable, int min, int max)
            throws Exception {
        return getStringCellValue(row, field, nullable, min, max, null, null);
    }

    /**
     * 以boolean格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空
     * @return 单元格内容
     * @throws Exception
     */
    public static Boolean getBooleanCellValue(Row row, String field, boolean nullable) throws Exception {
        Cell cell = getCell(row, field);
        if (cell == null) {// 单元格内容为空时，cell=null
            if (nullable == false) {
                throw new Exception(field + "不能为空");
            }
            return null;
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
            if (nullable == false)
                throw new Exception(field + "不能为空");
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_BOOLEAN) {
            return cell.getBooleanCellValue();
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING
                || cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {// XSSFCell与HSSFCell值相同
            if (nullable == false && StringUtils.isBlank(cell.getStringCellValue())) {
                throw new Exception(field + "不能为空");
            }
            return toBoolean(cell.getStringCellValue(), false);
        }
        throw new IllegalArgumentException("无法识别的boolean型字段(" + field + ")。");
    }

    /** 内置布尔值为真时的字符串数组。 */
    public static final String[] TRUE_STRINGS = new String[] { "true", "t", "1", "on", "yes", "y", "是" };
    /** 内置布尔值为假时的字符串数组。 */
    public static final String[] FALSE_STRINGS = new String[] { "false", "f", "0", "off", "no", "n", "否", "不", "非" };

    /**
     * 将字符串转换为Boolean类型。
     *
     * @param str
     *            字符串。
     * @param defaultValue
     *            默认取值。当传入true时，将严格检查字符串是否为{@link #FALSE_STRINGS}
     *            中之一（大小写不敏感），其它字符串包括null 将被返回Boolean.TRUE；当传入false时反之。
     */
    public static Boolean toBoolean(String str, boolean defaultValue) {
        if (StringUtils.isBlank(str)) {
            return Boolean.valueOf(defaultValue);
        }
        str = str.trim().toLowerCase();
        if (defaultValue) {
            for (String s : FALSE_STRINGS) {
                if (s.equals(str)) {
                    return Boolean.FALSE;
                }
            }
            return Boolean.TRUE;
        } else {
            for (String s : TRUE_STRINGS) {
                if (s.equals(str)) {
                    return Boolean.TRUE;
                }
            }
            return Boolean.FALSE;
        }
    }

    /**
     * 以数值格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @return 单元格内容
     * @throws Exception
     */
    public static BigDecimal getBigDecimalCellValue(Row row, String field) throws Exception {
        return getBigDecimalCellValue(row, field, true, BigDecimal.ZERO, true, BIGDECIMAL_MAXVALUE, true, -1);
    }

    /**
     * 以数值格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空。当单元格是空时，允许为空将返回null;否则抛出异常
     * @return 单元格内容
     * @throws Exception
     */
    public static BigDecimal getBigDecimalCellValue(Row row, String field, boolean nullable) throws Exception {
        return getBigDecimalCellValue(row, field, nullable, BigDecimal.ZERO, true, BIGDECIMAL_MAXVALUE, true, -1);
    }

    /**
     * 以数值格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空。当单元格是空时，允许为空将返回null;否则抛出异常
     * @param scale
     *            小数位数，-1表示不限制
     * @return 单元格内容
     * @throws Exception
     */
    public static BigDecimal getBigDecimalCellValue(Row row, String field, boolean nullable, int scale)
            throws Exception {
        return getBigDecimalCellValue(row, field, nullable, BigDecimal.ZERO, true, BIGDECIMAL_MAXVALUE, true, scale);
    }

    /**
     * 以数值格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空。当单元格是空时，允许为空将返回null;否则抛出异常
     * @param minValue
     *            最小值， 实际值应>=或>最小值（>=还是>由{@link #()}
     *            决定）。null表示不限制。
     * @param allowEqualsMin
     *            是否允许等于最小值
     * @param maxValue
     *            最大值， 实际值应<=或<最大值（<=还是<由{@link #()}
     *            决定）。null表示不限制。
     * @param allowEqualsMax
     *            是否允许等于最大值
     * @param scale
     *            小数位数，-1表示不限制
     * @return 单元格内容
     * @throws Exception
     */
    public static BigDecimal getBigDecimalCellValue(Row row, String field, boolean nullable, BigDecimal minValue,
                                                    boolean allowEqualsMin, BigDecimal maxValue, boolean allowEqualsMax, int scale) throws Exception {
        Cell cell = getCell(row, field);
        BigDecimal value;

        if (cell == null) {
            if (nullable == false) {
                throw new Exception(field + "不能为空");
            }
            return null;
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
            if (nullable == false)
                throw new Exception(field + "不能为空");
            return null;
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC
                || cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {// XSSFCell与HSSFCell值相同
            try {
                value = BigDecimal.valueOf(cell.getNumericCellValue());
            } catch (Exception e) {
                throw new Exception(field + "无法识别的数值格式");
            }
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
            String text = cell.getStringCellValue();
            try {
                value = new BigDecimal(text);
            } catch (Exception e) {
                throw new Exception("无法识别的字段(" + field + ")。");
            }
        } else {
            throw new Exception("无法识别的字段(" + field + ")。");
        }

        if (value == null)
            return null;

        if (scale != -1) {
            value = value.setScale(scale, BigDecimal.ROUND_HALF_UP);
        }

        if (minValue != null) {
            minValue = scale == -1 ? minValue : minValue.setScale(scale, BigDecimal.ROUND_UP);
            int compare = value.compareTo(minValue);
            if (compare < 0 || (!allowEqualsMin && compare == 0)) {
                throw new Exception(field + "应该大于" + (allowEqualsMin ? "等于" : "") + minValue);
            }
        }
        if (maxValue != null) {
            maxValue = scale == -1 ? maxValue : maxValue.setScale(scale, BigDecimal.ROUND_DOWN);
            int compare = value.compareTo(maxValue);
            if (compare > 0 || (!allowEqualsMax && compare == 0)) {
                throw new Exception(field + "应该小于" + (allowEqualsMax ? "等于" : "") + maxValue);
            }
        }
        return value;
    }

    public static Date getDateCellValue(Row row, String field) throws Exception {
        return getDateCellValue(row, field, true);
    }

    /**
     * 以日期格式获取单元格内容
     *
     * @param row
     *            行
     * @param field
     *            列标题
     * @param nullable
     *            是否允许为空。当单元格是空时，允许为空将返回null;否则抛出异常
     * @return 单元格内容
     * @throws Exception
     */
    public static Date getDateCellValue(Row row, String field, boolean nullable, String... formats) throws Exception {
        Cell cell = getCell(row, field);
        if (cell == null) {
            if (nullable == false) {
                throw new Exception(field + "不能为空");
            }
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {// XSSFCell与HSSFCell值相同
            // 时间格式
            if (HSSFDateUtil.isCellDateFormatted(cell)) {
                return cell.getDateCellValue();
            }
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
            if (nullable == false)
                throw new Exception(field + "不能为空");
            return null;
        } else if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING
                || cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
            String text = cell.getStringCellValue();
            if (nullable == false && StringUtils.isBlank(text))
                throw new Exception(field + "不能为空");
            try {
                return DateForIndUtils.toDate(text, formats);
//                return StringUtil.toDate(text, formats);
            } catch (Exception e) {
                throw new IllegalArgumentException("无法识别的字段(" + field + ")。");
            }
        }
        throw new Exception("无法识别的字段(" + field + ")。");
    }
}
